package com.fingard.dsp.bank.directbank.chinapay01;

import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.chinapay01.util.*;
import com.fingard.io.FileHelper;
import com.fingard.text.StringHelper;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

public class ChinaPay01Base extends DirectBase {
	protected String getCharset() {
		return getBankConfig().getCharset("UTF-8");
	}
	
	public static String genHtmlResult(Map<String, String> data){
    	TreeMap<String, String> tree = new TreeMap<String, String>();
		Iterator<Entry<String, String>> it = data.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			tree.put(en.getKey(), en.getValue());
		}
		it = tree.entrySet().iterator();
		StringBuffer sf = new StringBuffer();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			String key = en.getKey(); 
			String value =  en.getValue();
			if (sf.length() > 0) {
				sf.append("&");
			}
			sf.append(key + "=" + value);
		}
		
		return sf.toString();
    }
	
	protected Map<String, String> signByRSA(Map<String, String> reqData, ActSetItem actItem) throws Exception {
		reqData = SDKUtil.filterBlank(reqData);
		reqData.put("certId", getSignCertId(actItem));
		
		String signData = SDKUtil.coverMap2String(reqData);
		WriteBankLogLn2("签名原始数据:\r\n" + signData);
		
		String signature = sign(signData, actItem);
		reqData.put("signature", signature);
		
		return reqData;
	}
	
	public String getSignCertId(ActSetItem actItem) {
		try {
			KeyStore keyStore = CertUtil.getKeyInfo(actItem.ownKeyStorePath, actItem.ownKeyPassword, "PKCS12");
			Enumeration<String> aliasenum = keyStore.aliases();
			String keyAlias = null;
			if (aliasenum.hasMoreElements()) {
				keyAlias = aliasenum.nextElement();
			}
			X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias);
			return cert.getSerialNumber().toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * SHA256WithRSA签名
	 * 
	 * @param actItem
	 * @param p_reqStr
	 * @return
	 * @throws Exception 
	 */
	public String sign(String p_reqStr, ActSetItem actItem) throws Exception {  
		KeyStore ks = KeyStore.getInstance("PKCS12");
		FileInputStream fis = new FileInputStream(actItem.ownKeyStorePath);
		
		char[] keyPassword = null;
		if (StringHelper.hasAnyChar(actItem.ownKeyPassword)) {
			keyPassword = actItem.ownKeyPassword.toCharArray();
		}
		ks.load(fis, keyPassword);
		fis.close();
		fis = null;

		@SuppressWarnings("rawtypes")
		Enumeration enumAlias = ks.aliases();
		
		String keyAlias = null;
		if (enumAlias.hasMoreElements()) {
			keyAlias = (String) enumAlias.nextElement();
		}
		
		PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keyPassword);
		if (privateKey == null) {
			return null;
		}
		
		byte[] signDigest = SecureUtil.sha256X16(p_reqStr, getCharset());
		byte[] byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(privateKey, signDigest));
        
        return new String(byteSign);
    }  
	
	/**
	 * SHA256WithRSA验签
	 * 
	 * @param actItem
	 * @param p_reqStr
	 * @param signed
	 * @return
	 * @throws Exception
	 */
	public boolean verifySignByRSA(Map<String, String> p_reqData, ActSetItem actItem) throws Exception {  
		// 从HTTP响应数据获取公钥
		String strCert = p_reqData.get("signPubKeyCert");
		X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);
		if(x509Cert == null) {
			return false;
		}
		
		PublicKey publicKey = x509Cert.getPublicKey();
		if (publicKey == null) {
			return false;
		}
		
		// 3.验签
		String stringSign = p_reqData.get("signature");
		String verifyData = SDKUtil.coverMap2String(p_reqData);
		
		byte[] signBytes = SecureUtil.base64Decode(stringSign.getBytes(getCharset()));
		byte[] verifyBytes = SecureUtil.sha256X16(verifyData, getCharset());
		
		Signature signature = Signature.getInstance("SHA256withRSA");
		signature.initVerify(publicKey);
		signature.update(verifyBytes);
		
		return signature.verify(signBytes);
    }  
	
	/**
	 *  byte[] 转 16进制字符串
	 *  
	 * @param src
	 * @return
	 */
	public static String bytesToHexString(byte[] src) {  
        StringBuilder stringBuilder = new StringBuilder();  
        if (src == null || src.length <= 0) {  
            return null;  
        }  
        for (int i = 0; i < src.length; i++) {  
            int v = src[i] & 0xFF;  
            String hv = Integer.toHexString(v);  
            if (hv.length() < 2) {  
                stringBuilder.append(0);  
            }  
            stringBuilder.append(hv);  
        }  
        
        return stringBuilder.toString();  
    }  

	/**
	 * 发送数据到银行
	 * 
	 * @param p_reqmap
	 * @param actItem
	 * @param p_reqUrl
	 * @return
	 * @throws Exception
	 */
	public Map<String, String> sendToBank(Map<String, String> p_reqMap, ActSetItem actItem, String p_reqUrl) throws Exception {
		Map<String, String> tmpMapReq = signByRSA(p_reqMap, actItem);
		
		WriteBankLogStep2(genHtmlResult(tmpMapReq));
		WriteBankUrlLogLn(p_reqUrl);
		
		Map<String, String> tmpMapRet = AcpService.post(tmpMapReq, p_reqUrl, getCharset());
		WriteBankLogStep3(genHtmlResult(tmpMapRet));
		
		return tmpMapRet;
	}
	
	
	/**
	 * 获取公钥
	 * @throws Exception 
	 */
	public PublicKey getPubCerFile(String pubCerPath) throws Exception {
        FileInputStream fis = null;
        X509Certificate cert=null;
        try {
            fis = new FileInputStream(pubCerPath);
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            cert = (X509Certificate) cf.generateCertificate(fis);
            fis.close();

            PublicKey publicKey = cert.getPublicKey();
            if (publicKey == null) {
                return null;
            }
		    return publicKey;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
	}
	
	
	public PublicKey getEncryptCertPublicKey(ActSetItem actItem) {
		X509Certificate encryptCert = null;
		
		String path = actItem.ownCertFilePath;
		if (StringHelper.hasAnyChar(path)) {
			encryptCert = initCert(path);
			return encryptCert.getPublicKey();
		} else {
			return null;
		}
	}
	
	public String getEncryptCertId(ActSetItem actItem) {
		X509Certificate encryptCert = null;
		
		String path = actItem.ownCertFilePath;
		if (FileHelper.existFile(path)) {
			encryptCert = initCert(path);
			return encryptCert.getSerialNumber().toString();
		} else {
			LogUtil.writeErrorLog("acpsdk.encryptCert.path is empty");
			return null;
		}
	}
	
	 static{
		  try{
		   Security.addProvider(new BouncyCastleProvider());
		  }catch(Exception e){
		   e.printStackTrace();
		  }
		 }
	
	public X509Certificate initCert(String path) {
		X509Certificate encryptCertTemp = null;
		CertificateFactory cf = null;
		FileInputStream in = null;
		try {
			cf = CertificateFactory.getInstance("X.509", "BC");
			in = new FileInputStream(path);
			encryptCertTemp = (X509Certificate) cf.generateCertificate(in);
		} catch (CertificateException e) {
			e.printStackTrace();
			WriteBankLogLn(e);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			WriteBankLogLn(e);
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
			WriteBankLogLn(e);
		} finally {
			if (null != in) {
				try {
					in.close();
				} catch (IOException e) {
					WriteBankLogLn(e);
				}
			}
		}
		return encryptCertTemp;
	}
	
	public String encryptData(String data, String encoding, ActSetItem actItem) {
		return SecureUtil.encryptData(data, encoding, getEncryptCertPublicKey(actItem));
	}
	
	/**
	 * 返回HTTP字符串转MAP
	 * 
	 * @param plainStr
	 * @return
	 */
	public static Map<String,String> convertToMap(String tmpStr) {
		Map<String,String> params = new HashMap<String,String>();
		
		String[] nodes = tmpStr.split("&");
		for (String node : nodes) {
			int index = node.indexOf("=");
			String key = node.substring(0, index);
			String value = node.substring(index+1);
			params.put(key, value);
		}
		
		return params;
	}
	
	public String deCodeFileContent(Map<String, String> resData) {
		String filePath = null;
		String fileContent = resData.get("fileContent");
		if (StringHelper.hasAnyChar(fileContent)) {
			FileOutputStream out = null;
			try {
				byte[] fileArray = SDKUtil.inflater(SecureUtil.base64Decode(fileContent.getBytes(getCharset())));
				String fileDirectory = createTempFileDirectory();
				if (SDKUtil.isEmpty(resData.get("fileName"))) {
					filePath = fileDirectory + File.separator + resData.get("merId") + "_" + resData.get("batchNo") + "_" + resData.get("txnTime") + ".txt";
				} else {
					filePath = fileDirectory + File.separator + resData.get("fileName");
				}
				File file = new File(filePath);
				if (file.exists()) {
					file.delete();
				}
				file.createNewFile();
			    out = new FileOutputStream(file);
				out.write(fileArray, 0, fileArray.length);
				out.flush();
			} catch (UnsupportedEncodingException e) {
				LogUtil.writeErrorLog(e.getMessage(), e);
			} catch (IOException e) {
				LogUtil.writeErrorLog(e.getMessage(), e);
			}finally{
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return filePath;
	}
	
	/**
	 * 获取线程等待时间列表
	 * 
	 * @param tmpSleepTimeList
	 * @return
	 */
	public String[] getRealSleepTimeList(String tmpSleepTimeList) {
		String[] sleepTimeList = tmpSleepTimeList.split(",");
		
		StringBuilder realSleepTimeList = new StringBuilder();
		for (int i=0;i<sleepTimeList.length;i++) {
			if (realSleepTimeList.length() > 0) {
				realSleepTimeList.append(",");
			}
			if (i==0) {
				realSleepTimeList.append(sleepTimeList[0]);
			} else {
				int sleep = Integer.parseInt(sleepTimeList[i-1]);
				int lastSleep = Integer.parseInt(sleepTimeList[i]);
				int realSleepTime = lastSleep - sleep;
				realSleepTimeList.append(realSleepTime);
			}
		}  
		return realSleepTimeList.toString().split(",");
	}
}
